package org.NooLab.docserver.properties;


import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
import org.w3c.dom.Node;

import org.NooLab.docserver.util.XmlStringHandling;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.strings.ArrUtilities;
import org.NooLab.utilities.strings.StringsUtil;



public class DocoControlProperties implements Serializable{

	private static final long serialVersionUID = -7608394827081943247L;

	DocumentObserverProperties docoProperties;
	
	String rootdir ="", projectName="";
	String settingsDir="" ,settingsFile="";

	ArrayList<String> addExcludes,addIncludes ,addSimpleIncludes;
	
	ControlSettingsSection 	locationExcluded = null,
							locationIncluded = null,
							typesIncluded = null ,
							typesExcluded = null ,
							includedExtensions=null,
							excludedExtensions=null,
							patternsExcluded = null;
	
	int folderSelectionConstraintByExtension = 0;
	double sizelimit=50000; // = 50 Mb
	
	transient XmlStringHandling xMsg = new XmlStringHandling() ;
	transient StringsUtil strgutil = new StringsUtil();
	transient ArrUtilities arrutil = new ArrUtilities ();
	transient DFutils fileutil = new DFutils();
	transient PrintLog out = new PrintLog(2,true);

	private String importfile="",importedLocations="";

	private boolean isConsiderOnlyIncludedExtensions;

	// ========================================================================
	public DocoControlProperties( DocumentObserverProperties props) {
		//
		docoProperties = props;
		
		addExcludes = new ArrayList<String>();
		addIncludes = new ArrayList<String>();
		addSimpleIncludes = new ArrayList<String>();
	}
	// ========================================================================

	public void readSettings(String string) throws Exception {
		
		
		rootdir = docoProperties.getSystemRootDir() ;
		projectName = docoProperties.getPersistenceSettings().getProjectName() ;
		
		// D:\data\iTexx\app\DocumentServer\itexx\config
		// D:/data/iTexx/app/DocumentServer/app/DocumentServer/itexx/config/docoserv-config.xml
		String prjstr = projectName;
		if (docoProperties.getApplicationContext().contentEquals( DocumentObserverProperties._APP_CONTEXT_ITEXX)){
			if ( (rootdir.indexOf("app/DocumentServer")<0) && 
				 (projectName.indexOf("app/DocumentServer")<0)){
				prjstr = "app/DocumentServer/"+projectName;
			}else{
				prjstr = projectName;
			}
		}
		settingsDir = DFutils.createPath( rootdir, prjstr+"/config");
		settingsFile = DFutils.createPath( settingsDir, "docoserv-config.xml");
		
		if (DFutils.fileExists(settingsFile)){
			
			readControlSettingsXml(settingsFile);
		} // file ?
		else{
			// msg...
			String estr = "docoserv-config.xml could not be loaded, file not found. \n"+
			              "  - rootdir      " + rootdir+"\n"+
			              "  - projectName  " + projectName +"\n"+
			              "  - attempted    " + settingsFile;
			throw(new Exception(estr)) ;
		}
		
	}

	private void readControlSettingsXml(String xFile) {
		String rawXmlStr="",str, priostr  ;
		String[] strv, strr ;
		
		ArrayList<String> _list ;
		Vector<Object> itemTagsContentList, nodesObjList ;
		Node node;
		
		
		
		locationIncluded = new ControlSettingsSection ();
		// note that an excluded directory normally excludes all its sub-directories,
		// yet, some could be included instead 
		locationExcluded = new ControlSettingsSection ();
		typesIncluded    = new ControlSettingsSection ();
		typesExcluded    = new ControlSettingsSection ();
		includedExtensions = new ControlSettingsSection ();
		excludedExtensions = new ControlSettingsSection ();
		patternsExcluded   = new ControlSettingsSection ();
		
		try {
			
			rawXmlStr = fileutil.readFile2String(xFile);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		if (xMsg.isXML(rawXmlStr)==false){
			return;
		}
		
		xMsg.clear() ;
		xMsg.setContentRoot( "doco") ;
		
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/doco", "context") ;
		

		str = xMsg.getSpecifiedInfo(rawXmlStr, "/general/operation","mode") ;
		// not used so far
			
		 
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/general/appcontext","mode") ;
		// "itexx" or "standalone"
		      str = str.toLowerCase().trim();
		      if (str.length()==0){
		    	  str = "standalone";
		      }
		      setApplicationContext(str) ;
		
		// import simplefile="D:/data/iTexx/locations.txt" />
		   
		      
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/general/import","simplefile") ;
		      if (str.length()>0){
		    	  str = str.trim() ;
		    	  if (fileutil.fileexists(str)){
		    		  importfile=str;
		    		  readImportFile();
		    	  }
		      }
		      
		      
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/general/select/extensions","onlyincluded") ;
		isConsiderOnlyIncludedExtensions = xMsg.getBool(str, true);
			    
		        
		strv = new String[2];
		strv[0] = "name";
		strv[1] = "priority";

		
		
		nodesObjList = xMsg.getNodeList(rawXmlStr, "/doco/include/type", "item") ;
					   _digestNodesObjList(nodesObjList, strv, typesIncluded);

		nodesObjList = xMsg.getNodeList(rawXmlStr, "/doco/exclude/type", "item") ;
					   _digestNodesObjList(nodesObjList, strv, typesExcluded);

		nodesObjList = xMsg.getNodeList(rawXmlStr, "/doco/exclude/location/patterns", "item") ;
						   
							strv = new String[2];
							strv[0] = "name";
							strv[1] = "string";
					   _digestNodesObjList(nodesObjList, strv, patternsExcluded);
						   
		strv = new String[5];
		strv[0] = "name";
		strv[1] = "drive";
		strv[2] = "basefolder";
		strv[3] = "sub";
		strv[4] = "priority";

		nodesObjList = xMsg.getNodeList(rawXmlStr, "/doco/include/location", "item") ;
					   _digestNodesObjList(nodesObjList, strv, locationIncluded);  

					   if (addIncludes.size()>0){
						   _importSimpleList(addIncludes,strv, locationIncluded,"local","1","5") ;
					   }
					   if (addSimpleIncludes.size()>0){
						   _importSimpleList(addSimpleIncludes,strv, locationIncluded,"local","0","5") ;
					   }
					   
        
		nodesObjList = xMsg.getNodeList(rawXmlStr, "/doco/exclude/location", "item") ;
					   _digestNodesObjList(nodesObjList, strv, locationExcluded);
					   //
					   if (addExcludes.size()>0){
						   _importSimpleList(addExcludes,strv, locationExcluded,"local","0","5") ;
					   }

					   
		// -----------
		    _list = _getListItems(rawXmlStr,"include","extensions");
		if (_list.size()>0){
			includedExtensions.attributes .addAll( _list );
			_ensurePrefix( includedExtensions.attributes, ".");
			priostr = xMsg.getSpecifiedInfo(rawXmlStr, "/include/extensions/items","priority") ;
			includedExtensions.priority = xMsg.getInt(priostr, 1) ;
		}
		
		       
	        _list = _getListItems(rawXmlStr,"exclude","extensions");
		if (_list.size()>0){
			excludedExtensions.attributes.addAll( _list );
			_ensurePrefix( excludedExtensions.attributes, ".");
			priostr = xMsg.getSpecifiedInfo(rawXmlStr, "/exclude/extensions/items","priority") ;
			excludedExtensions.priority = xMsg.getInt(priostr, 1) ;
		}

		
		// --------------------------------------------- 
		itemTagsContentList = xMsg.getItemsList(rawXmlStr, "/doco/exclude/type", "item", "name") ; // [image, video, sound, program]

		if (itemTagsContentList!=null){
			for (int i=0;i<itemTagsContentList.size();i++){
				str = (String)itemTagsContentList.get(i);
				// "[image, video, sound, program]"
				strr = str.split(";");
			}// i->
		}
		
		// <size limit="300" unit="M"/>
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/exclude/size","limit") ;
			  sizelimit = xMsg.getNum(str, 200.0);
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/exclude/size","unit") ;
		      str= str.toLowerCase();
		      if (str.length()==0){
		    	  str="k" ;
		      }
		      if (str.startsWith("b")){
		    	  sizelimit = sizelimit/1000.0;
		      }
		      if (str.startsWith("m")){
		    	  sizelimit = sizelimit*1000.0;
		      }else{
		    	  if (str.startsWith("k")){
		    		  sizelimit  =sizelimit*1.0;
		    	  }else{
		    		  if (str.startsWith("g")){
		    			  sizelimit = sizelimit * 1000000.0;
		    		  }
		    	  }
		      }
		      if (sizelimit>500000){ // absolute upper limit
		    	  sizelimit = 500000;
		      }
		      if (sizelimit<0.2){ // absolute upper limit
		    	  sizelimit = 0.2;
		      }
		
		str = xMsg.getSpecifiedInfo(rawXmlStr, "/include/location/constraint", "name", "extension", "specimen");
			  if ((str.length()>0) && (str.toLowerCase().contentEquals("mandatory"))){
				  folderSelectionConstraintByExtension = 1; 
			  }
		
		str=" ";
		
		
	}
	

	private void readImportFile() {
		String str = "",importedLocations = "";
		String[] loc;
		
		
		ArrayList<String> lines = new ArrayList<String>(fileutil.readplainFileintoItemList(importfile));
		
		for (int i=0;i<lines.size();i++){
			str = lines.get(i).trim() ;
			int p=str.indexOf(";");
			if ((p>=0) && (p<=1)){
				lines.set(i, "");
			}
		}
		 
		lines = arrutil.removeempty(lines);
		
		for (int i=0;i<lines.size();i++){
			str = lines.get(i);
			if (str.indexOf("=")<0){
				str="1="+str ;
			}
			loc = str.split("=");
			str = loc[1].trim() ;
			if (str.length()==0){
				continue;
			}
			if (loc[0].contentEquals( "-1")){
				addExcludes.add(str);
			}
			if (loc[0].contentEquals( "0")){
				addSimpleIncludes.add(str);
			}
			if (loc[0].contentEquals( "1")){
				addIncludes.add(str);
			}
			
		}// ->
		str=str+" ";
	}
	

	private void setApplicationContext(String apcoStr) {
		// 
		String str = apcoStr;
		
		if (str.contentEquals("standalone")){
			docoProperties.setApplicationContext( DocumentObserverProperties._APP_CONTEXT_ALONE );
		}
		if (str.startsWith("itex")){
			docoProperties.setApplicationContext( DocumentObserverProperties._APP_CONTEXT_ITEXX );	
		}
		
		
	}
	
	
	private void _ensurePrefix(ArrayList<String> items, String prefix) {
		// 
		
		if ((items==null) || (items.size()==0)){
			return;
		}
		
		for (int i=items.size()-1;i>0;i--){
			String str = items.get(i) ;
			str = strgutil.trimm(str,".");
			
			if ((str.length()>0) && (str.startsWith(".")==false)){
				str = "."+str;
				items.set(i, str) ;
			}
			if ((str.length()>0) && (str.endsWith(".") )){
				str = str.substring(0,str.length()-1).trim() ;
				items.set(i, str) ;
			}
			
			str = strgutil.replaceAll(str, new String[]{"/",",",";","\\","%","&","+"}, "", false);
			if (str.length()==0){
				items.remove(i);
			}
		}
		
	}
	
	
	private ArrayList<String> _getListItems(String rawXmlStr, String specsStr, String section ) {
	
		ArrayList<String> _items = new ArrayList<String>();
		String str,xloc ;
		String[] strv, strr ;
		
		Vector<Object> itemTagsContentList ;
					
		xloc = "/"+specsStr+"/"+section+"/items";
		
		str = xMsg.getSpecifiedInfo(rawXmlStr, xloc,"list") ; 			

		/*
		      <extensions>
		         <items list=".js;bak;tmp;.jpg;jpeg;tiff;png;bmp;" />
		         <item name="css;.java;" /> 
		      </extensions>
		*/
		xloc = "/doco/"+specsStr+"/"+section ;
		itemTagsContentList = xMsg.getItemsList(rawXmlStr, xloc, "item", "name") ;
		
			if (itemTagsContentList!=null){
			    if (str.endsWith(";")){
			    	str = str.substring(0,str.length()-1);
			    }
				for (int i=0;i<itemTagsContentList.size();i++){
					str = str+ ";"+(String)itemTagsContentList.get(i);
					
				}// i->
				if (str.startsWith(";")){
			    	str = str.substring(1,str.length());
			    }
			} // ?
			if (str.length()>0){
				strr = str.split(";");
				if (strr.length>0){
					_items = new ArrayList<String>(Arrays.asList(strr));
				}
			}
			
			
		return _items;
	}
		
	
	private void _importSimpleList( ArrayList<String> simpleList, String[] strv,
								    ControlSettingsSection _section, 
								    String locremot, String incl, String prio) {
									// e.g.  ,"local","0","5"

		
		String locationStr="" ;
		ArrayList<String> _attrValues ;
		
		
		
		for (int i = 0; i < simpleList.size(); i++) {
			locationStr =  simpleList.get(i);
			
	
			_attrValues = new ArrayList<String>();
			// local, D:, D:/data/iTexx/app/Texx/test2/data/raw, 0, 5]
			_attrValues.add(locremot) ;
			_attrValues.add("") ;
			_attrValues.add(locationStr) ;
			_attrValues.add(incl) ;
			_attrValues.add(prio) ;
			
			_section.attrValues.add(_attrValues) ;
		}

		
	}

	
	/**
	 * creates an object from the array of results
	 * there could be several sets, each of which contains a list of several values (acc. to the attributes)  
	 * 
	 * @param nodesObjList
	 * @param strv
	 * @param _section
	 */
	private void _digestNodesObjList( Vector<Object> nodesObjList, String[] strv ,
									  ControlSettingsSection _section){
		
		String rawXmlStr="",str ;
		String[] strr ;
		
		ArrayList<String> _attrValues ; 
		Node node;

		String[] cats = new String[strv.length] ;
		System.arraycopy(strv, 0, cats, 0, strv.length);
		_section.attributes = new ArrayList<String>(Arrays.asList(cats));
		_section.attrValues = new ArrayList<ArrayList<String>>();
		
		if (nodesObjList != null) {

			
			strr = new String[ strv.length];

			for (int i = 0; i < nodesObjList.size(); i++) {
				node = (Node) nodesObjList.get(i);
				strr = xMsg.getAttrValuesForNode(node, "item", strv);
		
				_attrValues = new ArrayList<String>();
				for (int s=0;s<strr.length;s++){
					_attrValues.add(strr[s]) ;
				}
				_section.attrValues.add(_attrValues) ;
			}

	}


	}

	public ControlSettingsSection getLocationExcluded() {
		if (locationExcluded==null){
			locationExcluded = new ControlSettingsSection();
		}
		return locationExcluded;
	}

	public ControlSettingsSection getLocationIncluded() {
		if (locationIncluded==null){
			locationIncluded = new ControlSettingsSection();
		}
		return locationIncluded;
	}

	public ControlSettingsSection getTypesIncluded() {
		return typesIncluded;
	}

	public ControlSettingsSection getTypesExcluded() {
		return typesExcluded;
	}

	public ControlSettingsSection getIncludedExtensions() {
		if (includedExtensions==null){
			includedExtensions = new ControlSettingsSection();
		}
		return includedExtensions;
	}

	public ControlSettingsSection getExcludedExtensions() {
		if (excludedExtensions==null){
			excludedExtensions = new ControlSettingsSection();
		}
		return excludedExtensions;
	}

	public ControlSettingsSection getPatternsExcluded() {
		if (patternsExcluded==null){
			patternsExcluded = new ControlSettingsSection();
		}
		return patternsExcluded ;
	}

	public int getFolderSelectionConstraintByExtension() {
		return folderSelectionConstraintByExtension;
	}

	public void setFolderSelectionConstraintByExtension(int folderSelectionConstraintByExtension) {
		this.folderSelectionConstraintByExtension = folderSelectionConstraintByExtension;
	}

	public String getSettingsFile() {
		return settingsFile;
	}

	public String getProjectName() {
		return projectName;
	}

	public void setProjectName(String projectName) {
		this.projectName = projectName;
	}

	public double getSizelimit() {
		return sizelimit;
	}

	public void setSizelimit(double sizelimit) {
		this.sizelimit = sizelimit;
	}

	public boolean isConsiderOnlyIncludedExtensions() {
		return isConsiderOnlyIncludedExtensions;
	}

	public void setConsiderOnlyIncludedExtensions(boolean flag) {
		isConsiderOnlyIncludedExtensions = flag;
	}
	
}
